package com.yricky.atri.activity

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.*
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.media.ImageReader
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.widget.ImageView
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.RadioButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import com.yricky.atri.utils.yolov5.Recognition
import com.yricky.atri.utils.yolov5.YoloV5fp16Session
import com.yricky.atri.utils.yolov5.label
import com.yricky.atri.viewmodel.YoloV5SessionViewModel
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.gpu.GpuDelegate
import java.io.File

/**
 * @author Yricky
 * @date 2022/1/9
 */
class CameraActivity:ComponentActivity() {
    private val viewModel by lazy {
        ViewModelProvider(this)[YoloV5SessionViewModel::class.java]
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val viewState by viewModel.viewState.collectAsState()
            if(viewState.isPrepared){
                YoloResultView(Modifier.fillMaxSize(),viewState.originalBitmap,viewState.results)
            }
            Column(modifier = Modifier.padding(8.dp).background(Color(0xccffffff))) {
                Text(text = "avgTotalMs:${viewState.avgClassifyMs}ms")
                Text(text = "avgInterpreterMs:${viewState.avgInterpreterMs}ms")
                RadioButton(
                    selected = viewState.useDevice == YoloV5SessionViewModel.UseDevice.CPU,
                    onClick = {
                        viewModel.changeDevice(YoloV5SessionViewModel.UseDevice.CPU)
                        prepareSession()
                    }
                )
                RadioButton(
                    selected = viewState.useDevice == YoloV5SessionViewModel.UseDevice.GPU,
                    onClick = {
                        viewModel.changeDevice(YoloV5SessionViewModel.UseDevice.GPU)
                        prepareSession()
                    }
                )
                RadioButton(
                    selected = viewState.useDevice == YoloV5SessionViewModel.UseDevice.NNApi,
                    onClick = {
                        viewModel.changeDevice(YoloV5SessionViewModel.UseDevice.NNApi)
                        prepareSession()
                    }
                )
            }
        }
    }

    override fun onStart() {
        super.onStart()
        if (allPermissionsGranted()) {
            prepare()
        } else {
            ActivityCompat.requestPermissions(
                this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
        }
    }

    override fun onStop() {
        super.onStop()
        cameraInst?.close()
        cameraInst = null
        session?.finish()
    }

    override fun onDestroy() {
        super.onDestroy()
        thread.looper.quitSafely()
    }

    private val thread = HandlerThread("rego").also{it.start()}
    private val handler by lazy { Handler(thread.looper) }
    var cameraInst:CameraDevice? = null
    var session: YoloV5fp16Session? = null

    private fun prepareSession(){
        session?.finish()
        session = YoloV5fp16Session(
            File("${intent.getStringExtra("path")}"),
            viewModel.viewState.value.useDevice.option,
            (0 until 80).map { "$it" }
        )
    }


    private fun prepare(){
        viewModel.updateState { it.copy(isPrepared = false) }
        prepareSession()
        cameraInst?.close()
        val cameraId = if(viewModel.viewState.value.useBackCam) "0" else "1"
        val imageReader by lazy{
            ImageReader.newInstance(320,320, ImageFormat.JPEG,1)
        }
        val targets = listOf(imageReader.surface)
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
            imageReader.setOnImageAvailableListener({
                if(session?.isBusy != false){
                    return@setOnImageAvailableListener
                }
                val data = it.acquireLatestImage().use {
                    val tmpbuf = it.planes[0].buffer
                    ByteArray(tmpbuf.remaining()).also{
                        tmpbuf.get(it)
                    }
                }
                val bitmap = BitmapFactory.decodeByteArray(data,0,data.size)
                val sizedBitmap = Bitmap.createBitmap(
                    YoloV5fp16Session.INPUT_SIZE, YoloV5fp16Session.INPUT_SIZE,
                    Bitmap.Config.ARGB_8888)
                Canvas(sizedBitmap).drawBitmap(bitmap, Rect(0,0,bitmap.width,bitmap.height),
                    Rect(0,0,sizedBitmap.width,sizedBitmap.height),Paint()
                )
                bitmap.recycle()
                val res = session?.recognizeImage(sizedBitmap) ?: return@setOnImageAvailableListener
                viewModel.updateState { vs->
                    vs.copy(
                        originalBitmap = sizedBitmap,
                        results = res.result,
                        avgInterpreterMs = res.interpreterMs,
                        avgClassifyMs = res.totalMs
                    )
                }
            }, handler)

            getCameraManager().openCamera(cameraId,object : CameraDevice.StateCallback(){
                override fun onOpened(camera: CameraDevice) {
                    camera.createCaptureSession(targets, object: CameraCaptureSession.StateCallback() {
                        override fun onConfigured(session: CameraCaptureSession) {
                            val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
                            captureRequest.addTarget(imageReader.surface)
                            session.setRepeatingRequest(captureRequest.build(), null, null)
                        }

                        override fun onConfigureFailed(session: CameraCaptureSession) = Unit
                    }, null)
                    cameraInst = camera

                }

                override fun onDisconnected(camera: CameraDevice) {
                    cameraInst = null
                    camera.close()
                }

                override fun onError(camera: CameraDevice, error: Int) {}
            },null)
        }
        viewModel.updateState { it.copy(isPrepared = true) }
    }






    @Composable
    private fun YoloResultView(modifier: Modifier = Modifier,bitmap: Bitmap,res:List<Recognition>){
        AndroidView(
            modifier = modifier,
            factory = {
                ImageView(it)
            },
            update = {
                it.setImageBitmap(bitmap.also { bm->
                    bm.label(res)
                })
            }
        )
    }

    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(
            baseContext, it) == PackageManager.PERMISSION_GRANTED
    }

    @SuppressLint("MissingSuperCall")
    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>, grantResults:
        IntArray) {
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                prepare()
            } else {
                Toast.makeText(this,
                    "Permissions not granted by the user.",
                    Toast.LENGTH_SHORT).show()
                finish()
            }
        }
    }

    private fun getCameraManager(): CameraManager {
        return getSystemService(Context.CAMERA_SERVICE) as CameraManager
    }


    companion object {
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
    }

}